package com.three.config.spring;

import com.google.common.collect.Maps;
import com.three.constant.Separator;
import com.three.exception.ServiceException;
import com.three.utils.PackageScanner;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.env.Environment;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;

/**
 * Created by wangziqing on 2017/5/21 0021.
 */
@Configuration
@DependsOn("packageScanner")
@Qualifier("configLoader")
public class ConfigLoader implements EnvironmentAware, InitializingBean {
    private static Map<String,Field> K_F= Maps.newHashMap();
    private static Environment environment;
    private static Logger logger = LoggerFactory.getLogger(ConfigLoader.class);
    @Override
    public void afterPropertiesSet() throws Exception {
        try {
            logger.warn("加载配置：————————————start————————————");
            logger.warn("加载配置：激活环境——{}",environment.getActiveProfiles());
            //解析映射类
            PackageScanner.clazzCollection.stream()
                    .forEach(this::loadClass);
            //注入值
            loadFiled();
            logger.warn("加载配置：————————————end—————————————");
        } catch (Exception e) {
            throw new ServiceException(e, "load config error");
        }
    }

    private void loadClass(Class clazz){
        if(Objects.nonNull(clazz)){
            Config config=(Config)clazz.getAnnotation(Config.class);
            if(Objects.nonNull(config)){
                logger.warn("加载配置：扫描到配置类——{}",clazz.getSimpleName());
                String prefix=StringUtils.isEmpty(config.name())?
                        clazz.getSimpleName().toLowerCase():config.name();
                loadClass(prefix,clazz);
            }
        }
    }
    private void loadClass(String prefix,Class clazz){
        Stream.of(clazz.getDeclaredFields()).forEach(field -> {
            String name=field.getName();
            Config config=field.getAnnotation(Config.class);
            if(Objects.nonNull(config)){
                name=config.name();
            }
            K_F.put(prefix+ Separator.POINT+name, field);
        });
        Stream.of(clazz.getClasses())
                .forEach(subClazz -> {
                    loadClass(prefix+Separator.POINT+subClazz.getSimpleName().toLowerCase(),subClazz);});
    }

    private void loadFiled(){
        K_F.entrySet().stream().forEach(entry -> {
            try{
                //对基本类型和String无效，原因是编译成字节码后，存储在方法区的常量池中
                //解决方法：配置初始值set null
                String value=environment.getProperty(entry.getKey());
                if(StringUtils.isEmpty(value)){
                    //找不到配置值默认给空字符串，避免null
                    value=StringUtils.EMPTY;
                }else{
                    setStaticFieldValue(entry.getValue(),value);
                }
                logger.warn("加载配置：注入属性，{}——{}",entry.getKey(),value);
            }catch (Exception e){
                logger.warn("加载配置：注入异常，key——{}",entry.getKey());
                throw new ServiceException(e,"set property value error,key:{}",entry.getKey());
            }
        });
    }
    @Override
    public void setEnvironment(Environment environment) {
        this.environment=environment;
    }
    //反射修改静态属性的值
    private void setStaticFieldValue(Field field,String value)throws Exception{
        Class clazz=field.getType();
        field.setAccessible(true);
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        if(clazz == Integer.class){
            Integer i=Integer.valueOf(value);
            field.set(null, i);
        }else if(clazz == Long.class){
            Long l=Long.valueOf(value);
            field.set(null, l);
        }else if(clazz == Boolean.class){
            Boolean b= value.equals("true");
            field.set(null, b);
        }else {
            field.set(null,value);
        }
    }
}
